using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;

namespace Server.Net
{
	public delegate void TcpAcceptEventHandler(Object sender, Socket socket);

	public class TcpServer
	{
		public event TcpAcceptEventHandler Accept;  

		protected TcpAsyncListener listener;

		private IPAddress address;
		private IPEndPoint endpoint;

		private bool stop = false;
		
		public TcpServer(int port)
		{
			this.endpoint = new IPEndPoint(IPAddress.Any, port);
		}

		public void Start()
		{
			listener = new TcpAsyncListener(endpoint);
			listener.Start();
			listener.BeginAcceptSocket(new AsyncCallback(this.OnAcceptSocket), listener);
			
			endpoint.Port = listener.LocalEndPoint.Port;
			
		}
		
		protected virtual void OnAcceptSocket(IAsyncResult result)
		{
			try 
			{
				if ( stop == false ) 
				{
					TcpAsyncListener listener = (TcpAsyncListener)result.AsyncState;
					Socket socket = listener.EndAcceptSocket(result);

					listener.BeginAcceptSocket(new AsyncCallback(this.OnAcceptSocket), listener);
					if (socket != null) 
					{
						if (null != Accept) 
						{
							Accept(this, socket);
						}
						else 
						{
							socket.Shutdown(SocketShutdown.Both);
							socket.Close();
						}
					}
				}
			} 
			catch (Exception e) 
			{
				Console.WriteLine(e.ToString());
			}
		}
    
		public virtual void Stop()
		{
			stop = true;
			this.listener.Stop();
      		}
	}

	public class TcpAsyncListener : TcpListener
	{

		private delegate Socket AcceptSocketDelegate();

		private delegate TcpClient AcceptTcpClientDelegate();
    
		private AcceptSocketDelegate acceptSocket;

		private AcceptTcpClientDelegate acceptTcpClient;

		public TcpAsyncListener(IPEndPoint endpoint) : base(endpoint)
		{
		}

		public TcpAsyncListener(IPAddress address, int port) : base(address,port)
		{
		}
    
		public IAsyncResult BeginAcceptSocket(AsyncCallback callback,Object state)
		{
			if (acceptSocket == null)
				acceptSocket = new AcceptSocketDelegate(this.AcceptSocket);

			return acceptSocket.BeginInvoke(callback, state);
		}

		public IAsyncResult BeginAcceptTcpClient(AsyncCallback callback,Object state)
		{
			if (null == acceptTcpClient)
				acceptTcpClient = new AcceptTcpClientDelegate(this.AcceptTcpClient);
      
			return acceptTcpClient.BeginInvoke(callback,state);
		}


		// The code seems to hang right here.. But it works with 0.26
		//
		//
		public Socket EndAcceptSocket(IAsyncResult result)
		{
			Console.WriteLine("{0}: Calling EndInvoke", "EndAcceptSocket");
			Socket s = acceptSocket.EndInvoke(result);
			Console.WriteLine("{0}: EndInvoke finished", "EndAcceptSocket");
			return s;
		}

		public TcpClient EndAcceptTcpClient(IAsyncResult result)
		{
			return acceptTcpClient.EndInvoke(result);
		}

		public IPEndPoint LocalEndPoint
		{
			get {
				return (IPEndPoint)this.Server.LocalEndPoint;
			}
		}
	}
}